; 8051 Disassembly of SB DSP version 2.02
; Reversed by @TubeTimeUS, 2020/11/13.
; Comments (C) TubeTime

;
; Register/Memory Equates
;
.EQU ram_samples_x2, 10h
.EQU ram_pb_count, 11h
.EQU ram_pb_count2, 12h
.EQU ram_pb_unused, 13h
.EQU ram_loops2, 14h
.EQU ram_loops, 15h
.EQU ram_pb_unused2, 16h
.EQU rb2r7, 17h
.EQU time_constant, 18h
.EQU rb3r1, 19h
.EQU ram_smps_left, 1ah
.EQU length_low, 1bh
.EQU length_high, 1ch
.EQU dma_blk_len_lo, 1dh
.EQU dma_blk_len_hi, 1eh
.EQU command_byte, 20h
.EQU len_left_lo, 21h
.EQU len_left_hi, 22h
.EQU status_register, 23h
.EQU dsp_dma_id0, 25h
.EQU dsp_dma_id1, 26h
.EQU vector_low, 2ch
.EQU vector_high, 2dh
.EQU warmboot_magic1, 2eh
.EQU warmboot_magic2, 2fh

;
; SFR Equates
;
.EQU port_dac_out, 90h

;
; SFR bit Equates
;
.EQU pin_dac_0, 90h
.EQU pin_dac_1, 91h
.EQU pin_dac_2, 92h
.EQU pin_dac_3, 93h
.EQU pin_dac_4, 94h
.EQU pin_dac_5, 95h
.EQU pin_dac_6, 96h
.EQU pin_dac_7, 97h
.EQU pin_mute_en, 0a0h
.EQU pin_adc_comp, 0a5h
.EQU pin_dav_pc, 0a6h
.EQU pin_dav_dsp, 0a7h
.EQU pin_irequest, 0b2h
.EQU pin_dsp_busy, 0b3h
.EQU pin_dma_enablel, 0b4h
.EQU pin_drequest, 0b5h

;
; Memory bit Equates
;
.EQU command_byte_0, 0
.EQU command_byte_1, 1
.EQU command_byte_2, 2
.EQU command_byte_3, 3
.EQU command_byte_4, 4
.EQU command_byte_5, 5
.EQU command_byte_6, 6
.EQU command_byte_7, 7
.EQU spkr_on, 18h
.EQU mode_dma_adc, 1ah
.EQU mode_silence, 1bh
.EQU mode_adpcm2, 1ch
.EQU mode_adpcm26, 1dh
.EQU mode_adpcm4, 1eh
.EQU mode_dma_dac, 1fh
.EQU cmd_avail, 20h
.EQU dma_mode_on, 21h
.EQU dma_autoinit_on, 22h
.EQU midi_timestamp, 23h
.EQU autoinit_exit, 24h
.EQU high_speed, 25h
.EQU record_mode, 26h

	.org	0

RESET:	ljmp	start

	.db	0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh

	.org	0bh
;
; Timer/Counter 0 Interrupt Vector
;
TF0_VECTOR:
	; High speed flag indicates fast sample playback/record mode
	jb	high_speed,hs_int
	; MIDI timestamp flag 
	jb	midi_timestamp,midi_timestamp_int

	; Perform housekeeping and jump to appropriate handler for the current
	; DSP mode.
	setb	pin_dsp_busy
	push	acc
	push	dpl
	push	dph
	mov	dpl,vector_low
	mov	dph,vector_high
	clr	a
	jmp	@a+dptr
;
; Handles MIDI timestamp counter.
;
midi_timestamp_int:
	inc	r5
	cjne	r5,#0,X002a
	inc	r6
	cjne	r6,#0,X002a
	inc	r7
X002a:	mov	tl0,#17h
	mov	th0,#0fch
	reti	
;
; Handle fast sample playback/record modes. These did not exist in the
; original SB firmware and were added later, removing interrupt overhead
; to allow for higher sample rates.
;
hs_int:
	; Check if we are in record mode
	jb	record_mode,get_adc_sample
	; Ask host PC for a sample
	setb	pin_drequest
	clr	pin_drequest
	; Wait for sample to arrive
X0038:	jnb	pin_dav_dsp,X0038
	; Read the mailbox and forward the sample to the DAC
	movx	a,@r0
	mov	port_dac_out,a
	; Check if we have more samples to output
	cjne	r6,#0,X0064
	cjne	r7,#0,X0063
	; We ran out of samples to play back, so trigger an interrupt to let
	; the host PC know about it.
	clr	pin_irequest
	setb	pin_irequest
	; Check for autoinit mode (command 91h)
	jb	command_byte_0,X0052
	; Reinitialize the sample counter to our DMA block length
	mov	r6,dma_blk_len_lo
	mov	r7,dma_blk_len_hi
	ljmp	X0065
;
X0052:	
	; Reset the timer
	clr	et0
	clr	tr0
	; Disable DMA
	setb	pin_dma_enablel
	; Clear warm boot magic number
	mov	warmboot_magic1,#0
	mov	warmboot_magic2,#0
	; Turn off high speed mode
	clr	high_speed
	ljmp	X0065

	; Decrement sample counter
X0063:	dec	r7
X0064:	dec	r6
X0065:	reti	
;
; Collects a sample from the microphone input. This routine uses a SAR
; algorithm (successive approximation).
;
get_adc_sample:
	; Set the DAC to center code
	mov	port_dac_out,#80h
	nop	
	nop
	; Check the analog comparator output	
	jnb	pin_adc_comp,X0070
	clr	pin_dac_7		; Below 80h
X0070:	setb	pin_dac_6		; Above 80h
	nop	
	nop
	; Repeat for the remaining bits	
	jnb	pin_adc_comp,X0079
	clr	pin_dac_6
X0079:	setb	pin_dac_5
	nop	
	jnb	pin_adc_comp,X0081
	clr	pin_dac_5
X0081:	setb	pin_dac_4
	nop	
	jnb	pin_adc_comp,X0089
	clr	pin_dac_4
X0089:	setb	pin_dac_3
	nop	
	jnb	pin_adc_comp,X0091
	clr	pin_dac_3
X0091:	setb	pin_dac_2
	nop	
	jnb	pin_adc_comp,X0099
	clr	pin_dac_2
X0099:	setb	pin_dac_1
	nop	
	jnb	pin_adc_comp,X00a1
	clr	pin_dac_1
X00a1:	setb	pin_dac_0
	nop	
	jnb	pin_adc_comp,X00a9
	clr	pin_dac_0
	; the DAC output contains the acquired analog voltage now
X00a9:	mov	a,port_dac_out
	; Wait for the mailbox to become empty
X00ab:	jb	pin_dav_pc,X00ab
	; Write sample out to mailbox
	movx	@r0,a
	; Request DMA from the host PC
	setb	pin_drequest
	clr	pin_drequest
	; Check sample counter
	cjne	r6,#0,X00d9
	cjne	r7,#0,X00d8
	; Trigger interrupt if our sample count hit zero
	clr	pin_irequest
	setb	pin_irequest
	; Check if we are in auto-init DMA mode (command 99h)
	jb	command_byte_0,X00c7
	; Reset sample counter and start again
	mov	r6,dma_blk_len_lo
	mov	r7,dma_blk_len_hi
	ljmp	X00da
;
X00c7:	
	; Regular DMA
	; Clear timer, turn off DMA, clear warm boot magic number, and clear
	; high speed mode.
	clr	et0
	clr	tr0
	setb	pin_dma_enablel
	mov	warmboot_magic1,#0
	mov	warmboot_magic2,#0
	clr	high_speed
	ljmp	X00da

	; Decrement sample counter
X00d8:	dec	r7
X00d9:	dec	r6
X00da:	reti	

;
; Programmable interrupt vectors (non-high-speed)
; These are set up by placing their start address in vector_low and vector_high
; so that they will be triggered when the timer overflows.
;

;
; Vector for DAC playback, 8-bit DMA mode
;
vector_dma_dac_8:
	; Is the mailbox already full?
	jnb	pin_dav_dsp,X00e3
	; Then we have a command waiting
	setb	cmd_avail
	ljmp	vector_dma_dac_8_end
;
X00e3:	
	; Ask host PC for a sample
	setb	pin_drequest
	clr	pin_drequest
	; Wait for the data to arrive
X00e7:	jnb	pin_dav_dsp,X00e7
	; Copy it over to the DAC
	movx	a,@r0
	mov	port_dac_out,a
	; Check the remaining number of samples
	clr	a
	cjne	a,len_left_lo,X0132
	cjne	a,len_left_hi,X0130
	; If we are supposed to exit auto-init DMA, then do that.
	jb	autoinit_exit,X00fd
	; Are we in regular DMA mode? Then handle that.
	jb	dma_mode_on,X011f
	; Auto init is set, so handle that.
	jb	dma_autoinit_on,X0112
X00fd:	
	; Clean up after the end of auto-init DMA
	clr	dma_mode_on
	clr	dma_autoinit_on
	clr	autoinit_exit
	clr	et0
	clr	tr0
	; Trigger host PC interrupt since we are done.
	clr	pin_irequest
	setb	pin_irequest
	clr	mode_dma_dac
	; Turn off DMA
	setb	pin_dma_enablel
	; Done!
	ljmp	vector_dma_dac_8_end
;
X0112:
	; For ongoing auto-init DMA, loop back to the beginning
	; Reset the sample count
	mov	len_left_lo,dma_blk_len_lo
	mov	len_left_hi,dma_blk_len_hi
	; Trigger host PC interrupt
	clr	pin_irequest
	setb	pin_irequest
	; Return and continue playback
	ljmp	vector_dma_dac_8_end
;
X011f:	
	; Clean up after standard DMA mode
	clr	dma_autoinit_on
	clr	dma_mode_on
	; Reset sample count
	mov	len_left_lo,length_low
	mov	len_left_hi,length_high
	; Trigger host PC interrupt
	clr	pin_irequest
	setb	pin_irequest
	ljmp	vector_dma_dac_8_end

	; Decrement sample counter
X0130:	dec	len_left_hi
X0132:	dec	len_left_lo
vector_dma_dac_8_end:
	; We are done, so clear the busy bit and wait for the next command.
	clr	pin_dsp_busy
	pop	dph
	pop	dpl
	pop	acc
	reti	

;
; Vector for DAC playback, 2-bit ADPCM
;

; Register use:
; r3 = Packed samples remaining in current data byte
; r6 = Current data byte (contains 4 packed samples)
vector_dma_dac_adpcm2:
	; Is there a byte waiting in the mailbox already?
	jnb	pin_dav_dsp,X0145
	; Then it is a command, so quit.
	setb	cmd_avail
	ljmp	vector_dma_dac_adpcm2_end

	; First we need figure out if we need to request another
	; byte of sample data. r3 is that counter
X0145:	dec	r3
	; If it hits 0, then we need to get more data.
	cjne	r3,#0,vector_dma_dac_adpcm2_shiftin
	; Do we have more data bytes left to collect?
	clr	a
	cjne	a,len_left_lo,X01a4
	cjne	a,len_left_hi,X01a2
	; No more data bytes, so end playback depending on the mode that
	; we are in.
	jb	autoinit_exit,X0159
	jb	dma_mode_on,X0186
	jb	dma_autoinit_on,X016e
X0159:
	; End auto-init playback mode.
	clr	dma_mode_on
	clr	dma_autoinit_on
	clr	autoinit_exit
	clr	et0
	clr	tr0
	clr	pin_irequest
	setb	pin_irequest
	clr	mode_adpcm2
	setb	pin_dma_enablel
	ljmp	vector_dma_dac_adpcm2_end
;
X016e:	
	; For auto init DMA mode, reset sample counter, restart playback at the
	; beginning.
	mov	len_left_lo,dma_blk_len_lo
	mov	len_left_hi,dma_blk_len_hi
	; Ask for data byte
	setb	pin_drequest
	clr	pin_drequest
	; Wait for it to arrive
X0178:	jnb	pin_dav_dsp,X0178
	movx	a,@r0
	; Load it into r6 (data buffer)
	mov	r6,a
	; Since this is 2-bit ADPCM, each byte contains 4 samples.
	mov	r3,#4
	; Trigger host PC interrupt
	clr	pin_irequest
	setb	pin_irequest
	ljmp	vector_dma_dac_adpcm2_end
;
X0186:
	; Exit after finishing standard DMA output mode
	clr	dma_autoinit_on
	clr	dma_mode_on
	mov	len_left_lo,length_low
	mov	len_left_hi,length_high
	setb	pin_drequest
	clr	pin_drequest
X0194:	jnb	pin_dav_dsp,X0194
	movx	a,@r0
	mov	r6,a
	mov	r3,#4
	clr	pin_irequest
	setb	pin_irequest
	ljmp	vector_dma_dac_adpcm2_end

	; OK, since we are in the middle of playback, and we are supposed to
	; collect another data byte, the first thing to do is decrement the
	; bytes-left counter
X01a2:	dec	len_left_hi
X01a4:	dec	len_left_lo
	; We are reading in 4 samples at a time.
	mov	r3,#4
	; Ask for the samples from the host PC by triggering DMA.
	setb	pin_drequest
	clr	pin_drequest
	; Wait for the byte to arrive
X01ac:	jnb	pin_dav_dsp,X01ac
	; Copy it into our sample buffer
	movx	a,@r0
	mov	r6,a
vector_dma_dac_adpcm2_shiftin:
	; Decode the current sample
	lcall	adpcm_2_decode
vector_dma_dac_adpcm2_end:
	clr	pin_dsp_busy
	pop	dph
	pop	dpl
	pop	acc
	reti	

;
; Vector for DAC playback, 4-bit ADPCM
;

; Register use:
; r3 = Packed samples remaining in current data byte
; r6 = Current data byte (contains 4 packed samples)
vector_dma_dac_adpcm4:
	; If there's a byte waiting in the mailbox already, then it is a
	; a command, so go back to process that.
	jnb	pin_dav_dsp,X01c5
	setb	cmd_avail
	ljmp	vector_dma_dac_adpcm4_end
	; Figure out if we need to request another byte of sample data.
X01c5:	dec	r3
	; If r3 hits 0, then we need more data.
	cjne	r3,#0,vector_dma_dac_adpcm4_shiftin
	; Check to see if we have any remaining data bytes to collect.
	clr	a
	cjne	a,len_left_lo,X0224
	cjne	a,len_left_hi,X0222
	; We do not, so end playback depending on the mode that we are in.
	jb	autoinit_exit,X01d9
	jb	dma_mode_on,X0206
	jb	dma_autoinit_on,X01ee
X01d9:
	; Exit auto-init DMA mode.
	clr	dma_mode_on
	clr	dma_autoinit_on
	clr	autoinit_exit
	clr	et0
	clr	tr0
	clr	pin_irequest
	setb	pin_irequest
	clr	mode_adpcm4
	setb	pin_dma_enablel
	ljmp	vector_dma_dac_adpcm4_end

X01ee:
	; Ongoing auto-init DMA, so reset back to the beginning of the buffer
	; and continue playback from the start.
	mov	len_left_lo,dma_blk_len_lo
	mov	len_left_hi,dma_blk_len_hi
	setb	pin_drequest
	clr	pin_drequest
X01f8:	jnb	pin_dav_dsp,X01f8
	movx	a,@r0
	mov	r6,a
	; We have 2 packed samples per byte
	mov	r3,#2
	; Let the host PC know by triggering an interrupt
	clr	pin_irequest
	setb	pin_irequest
	ljmp	vector_dma_dac_adpcm4_end

X0206:
	; End of regular DMA playback, so clear flags, etc.
	clr	dma_autoinit_on
	clr	dma_mode_on
	mov	len_left_lo,length_low
	mov	len_left_hi,length_high
	setb	pin_drequest
	clr	pin_drequest
X0214:	jnb	pin_dav_dsp,X0214
	movx	a,@r0
	mov	r6,a
	mov	r3,#2
	clr	pin_irequest
	setb	pin_irequest
	ljmp	vector_dma_dac_adpcm4_end

	; OK, since we are in the middle of playback, and we are supposed to
	; collect another data byte, the first thing to do is decrement the
	; bytes-left counter
X0222:	dec	len_left_hi
X0224:	dec	len_left_lo
	; We are reading in 2 samples at a time.
	mov	r3,#2
	; Ask for the samples from the host PC by triggering DMA.
	setb	pin_drequest
	clr	pin_drequest
	; Wait for the byte to arrive
X022c:	jnb	pin_dav_dsp,X022c
	; Copy it into our sample buffer
	movx	a,@r0
	mov	r6,a
vector_dma_dac_adpcm4_shiftin:
	; Decode the current sample
	lcall	adpcm_4_decode
vector_dma_dac_adpcm4_end:
	clr	pin_dsp_busy
	pop	dph
	pop	dpl
	pop	acc
	reti	

;
; Vector for DAC playback, 2.6-bit ADPCM
; (I bet you can't wait to see how the fractional bit works!)
;

; Register use:
; r3 = Packed samples remaining in current data byte
; r6 = Current data byte (contains 2.6 packed samples)
vector_dma_dac_adpcm2_6:
	; If there's a byte waiting in the mailbox already, then it is a
	; a command, so go back to process that.
	jnb	pin_dav_dsp,X0245
	setb	cmd_avail
	ljmp	vector_dma_dac_adpcm2_6_end
	; Figure out if we need to request another byte of sample data.
X0245:	dec	r3
	; If r3 hits 0, then we need more data.
	cjne	r3,#0,vector_dma_dac_adpcm2_6_shiftin
	; Check to see if we have any remaining data bytes to collect.
	clr	a
	cjne	a,len_left_lo,X02a4
	cjne	a,len_left_hi,X02a2
	; We do not, so end playback depending on the mode that we are in.
	jb	autoinit_exit,X0259
	jb	dma_mode_on,X0286
	jb	dma_autoinit_on,X026e
X0259:	
	; Exit auto-init DMA mode.
	clr	dma_mode_on
	clr	dma_autoinit_on
	clr	autoinit_exit
	clr	et0
	clr	tr0
	clr	pin_irequest
	setb	pin_irequest
	clr	mode_adpcm26
	setb	pin_dma_enablel
	ljmp	vector_dma_dac_adpcm2_6_end

X026e:	
	; Ongoing auto-init DMA, so reset back to the beginning of the buffer
	; and continue playback from the start.
	mov	len_left_lo,dma_blk_len_lo
	mov	len_left_hi,dma_blk_len_hi
	setb	pin_drequest
	clr	pin_drequest
X0278:	jnb	pin_dav_dsp,X0278
	movx	a,@r0
	mov	r6,a
	; We have 3 packed samples per byte
	mov	r3,#3
	; Let the host PC know by triggering an interrupt
	clr	pin_irequest
	setb	pin_irequest
	ljmp	vector_dma_dac_adpcm2_6_end

X0286:	
	; End of regular DMA playback, so clear flags, etc.
	clr	dma_autoinit_on
	clr	dma_mode_on
	mov	len_left_lo,length_low
	mov	len_left_hi,length_high
	setb	pin_drequest
	clr	pin_drequest
X0294:	jnb	pin_dav_dsp,X0294
	movx	a,@r0
	mov	r6,a
	mov	r3,#3
	clr	pin_irequest
	setb	pin_irequest
	ljmp	vector_dma_dac_adpcm2_6_end

	; OK, since we are in the middle of playback, and we are supposed to
	; collect another data byte, the first thing to do is decrement the
	; bytes-left counter
X02a2:	dec	len_left_hi
X02a4:	dec	len_left_lo
	; We are reading in 3 samples at a time (2 full and 1 partial).
	mov	r3,#3
	; Ask for the samples from the host PC by triggering DMA.
	setb	pin_drequest
	clr	pin_drequest
	; Wait for the byte to arrive
X02ac:	jnb	pin_dav_dsp,X02ac
	; Copy it into our sample buffer
	movx	a,@r0
	mov	r6,a
vector_dma_dac_adpcm2_6_shiftin:
	; Decode the current sample
	lcall	adpcm_2_6_decode
vector_dma_dac_adpcm2_6_end:
	clr	pin_dsp_busy
	pop	dph
	pop	dpl
	pop	acc
	reti	
;
; Vector for DAC playback of silence. (Yes, it has its own vector.)
;
vector_dac_silence:
	; If there's a byte waiting in the mailbox already, then it is a
	; a command, so go back to process that.
	jnb	pin_dav_dsp,X02c5
	setb	cmd_avail
	ljmp	vector_dac_silence_end
X02c5:	
	; Check to see if we have any remaining samples to play.
	clr	a
	cjne	a,len_left_lo,X02dd
	cjne	a,len_left_hi,X02db
	; We do not, so end this playback operation.
	clr	et0
	clr	tr0
	clr	pin_irequest
	setb	pin_irequest
	clr	mode_silence
	setb	pin_dma_enablel
	ljmp	vector_dac_silence_end

	; Decrement the samples remaining counter.
X02db:	dec	len_left_hi
X02dd:	dec	len_left_lo
	; Normally we would request samples from the host PC, but in this case
	; we are playing silence, so we never request data nor do we update the
	; DAC output.
vector_dac_silence_end:
	clr	pin_dsp_busy
	pop	dph
	pop	dpl
	pop	acc
	reti	

;
; Vector for 8-bit DMA recording.
;
vector_dma_adc:
	; If there's a byte waiting in the mailbox already, then it is a
	; a command, so go back to process that.
	jnb	pin_dav_dsp,X02f0
	setb	cmd_avail
	ljmp	vector_dma_adc_end

X02f0:	
	; Acquire a sample using a SAR algorithm.
	; Start off the DAC at half code, then go through bit-by-bit, checking
	; the output of the analog comparator to decide how to change the DAC
	; output.
	mov	port_dac_out,#80h
	nop	
	nop	
	jnb	pin_adc_comp,X02fa
	clr	pin_dac_7
X02fa:	setb	pin_dac_6
	nop	
	nop	
	jnb	pin_adc_comp,X0303
	clr	pin_dac_6
X0303:	setb	pin_dac_5
	nop	
	jnb	pin_adc_comp,X030b
	clr	pin_dac_5
X030b:	setb	pin_dac_4
	nop	
	jnb	pin_adc_comp,X0313
	clr	pin_dac_4
X0313:	setb	pin_dac_3
	nop	
	jnb	pin_adc_comp,X031b
	clr	pin_dac_3
X031b:	setb	pin_dac_2
	nop	
	jnb	pin_adc_comp,X0323
	clr	pin_dac_2
X0323:	setb	pin_dac_1
	nop	
	jnb	pin_adc_comp,X032b
	clr	pin_dac_1
X032b:	setb	pin_dac_0
	nop	
	jnb	pin_adc_comp,X0333
	clr	pin_dac_0
	; We have finishing acquiring the sample, which is now contained in the
	; DAC output register.
X0333:	mov	a,port_dac_out
	; Wait for the mailbox to empty out (PC must read any stale data first)
X0335:	jb	pin_dav_pc,X0335
	; Now place the sample into the mailbox.
	movx	@r0,a
	; And request a DMA transfer from the host PC.
	setb	pin_drequest
	clr	pin_drequest
	; Check the sample counter to see if we have any more samples to record
	clr	a
	cjne	a,len_left_lo,X0385
	cjne	a,len_left_hi,X0383
	; We do not, so end recording in the way required by our current
	; recording mode.
	jb	autoinit_exit,X034d
	jb	dma_mode_on,X0372
	jb	dma_autoinit_on,X0365
X034d:	
	; We are exiting auto-init DMA mode.
	clr	dma_mode_on
	clr	dma_autoinit_on
	clr	autoinit_exit
	clr	et0
	clr	tr0
	; Wait for host PC to grab the last remaining sample.
X0357:	jb	pin_dav_pc,X0357
	; Trigger the host PC interrupt to tell it that we are done.
	clr	pin_irequest
	setb	pin_irequest
	clr	mode_dma_adc
	setb	pin_dma_enablel
	ljmp	vector_dma_adc_end
;
X0365:	
	; We are in auto-init DMA mode, so reset the sample counter and
	; continue recording samples starting at the beginning again.
	mov	len_left_lo,dma_blk_len_lo
	mov	len_left_hi,dma_blk_len_hi
	; Let the host PC know we hit the end of the buffer by triggering an
	; interrupt.
	clr	pin_irequest
	setb	pin_irequest
	ljmp	vector_dma_adc_end

X0372:	
	; End normal DMA mode.
	clr	dma_autoinit_on
	clr	dma_mode_on
	mov	len_left_lo,length_low
	mov	len_left_hi,length_high
	clr	pin_irequest
	setb	pin_irequest
	ljmp	vector_dma_adc_end

	; We still have samples left to record, so decrement the sample counter
	; and continue.
X0383:	dec	len_left_hi
X0385:	dec	len_left_lo
vector_dma_adc_end:
	clr	pin_dsp_busy
	pop	dph
	pop	dpl
	pop	acc
	reti	

;
; Vector for DAC playback from onboard SRAM
; (Formerly undocumented playback mode!)
;

; Register use:
; r1 = pointer to SRAM for current sample
vector_cmd_ram_playback:
	; Get sample from SRAM, send it out to the DAC.
	mov	a,@r1
	mov	port_dac_out,a
	; Move the sample pointer to the next sample in SRAM.
	inc	r1
	; Do we have any samples left to play back?
	djnz	ram_smps_left,vector_cmd_ram_playback_end
	; We do not, so reset the sample pointer.
	mov	r1,#40h
	; Decrement our playback loop counter. This is the number of times we
	; are supposed to play back the SRAM buffer.
	djnz	ram_loops,X039f
	mov	ram_loops,ram_pb_count2
X039f:	djnz	ram_loops2,vector_cmd_ram_playback_end
	; Playback loop counter reached zero, so end playback.
	clr	tr0
	clr	et0
vector_cmd_ram_playback_end:
	clr	pin_dsp_busy
	pop	dph
	pop	dpl
	pop	acc
	reti	
;
; Vector for sine wave playback
;

; Register use:
; r1 = pointer to sine wave table in SRAM (from 0x40 to 0x7F)
; r7 = increment value for table pointer

; Note: this algorithm attempts to implement some sort of DDS by using the r1
; register as a phase accumulator. However, the wraparound checks seem to be
; broken.
vector_sine:
	; Get sample from ROM, send it out to the DAC.
	mov	a,@r1
	mov	port_dac_out,a
	; Calculate where in the table to fetch the next sample from.
	mov	a,r7
	add	a,r1
	; Bounds check.
	cjne	a,#7fh,X03ba
	ljmp	X03bc
;
X03ba:	jnc	X03c0
X03bc:	
	; Carry bit set (add operation overflowed) or the value was equal to
	; 7f, so update pointer to the new value.
	mov	r1,a
	ljmp	X03c2
;
X03c0:	
	; Carry bit not set, so we reset the pointer to zero.
	mov	r1,#40h
X03c2:	clr	pin_dsp_busy
	pop	dph
	pop	dpl
	pop	acc
	reti	
;
; **********************
; Start: Where we begin.
; **********************
;
start:	
	; We are busy right now (this bit can be read by the host PC in the
	; status register).
	setb	pin_dsp_busy
	; Configure the chip
	setb	pt0
	mov	sp,#30h
	clr	pin_drequest
	setb	pin_dma_enablel
	setb	wr
	setb	rd
	mov	scon,#42h
	mov	th1,#0feh
	mov	tl1,#0feh
	mov	tmod,#22h
	mov	pcon,#80h
	setb	tr1
	setb	ren
	; Check for the warm boot magic number
	mov	a,#34h
	cjne	a,warmboot_magic1,cold_boot
	mov	a,#12h
	cjne	a,warmboot_magic2,cold_boot
	; We are in a warm boot situation, so first off, clear the magic
	; number.
	mov	warmboot_magic1,#0
	mov	warmboot_magic2,#0
	; Is the speaker on?
	jnb	spkr_on,warm_boot
	; Then set the DAC to half code and turn off mute.
	mov	port_dac_out,#80h
	clr	pin_mute_en
	ljmp	warm_boot

;
; Cold boot startup.
;
cold_boot:
	; DAC set to half code.
	mov	port_dac_out,#80h
	; Initialize a bunch of variables.
	mov	rb2r7,#80h
	mov	ram_loops,#0
	mov	r7,#2
	mov	time_constant,#9ch
	mov	dsp_dma_id0,#0aah
	mov	dsp_dma_id1,#96h
	mov	r0,#40h
	mov	r1,#40h
	mov	r4,#40h
	mov	dma_blk_len_lo,#0ffh
	mov	dma_blk_len_hi,#7
	mov	status_register,#0
; Warm boot, so we skipped over some initialization.
warm_boot:
	; Configure the timer period based on our stored time constant (aka
	; sample rate).
	mov	a,time_constant
	mov	th0,a
	mov	tl0,a
	mov	24h,#0
	mov	r3,#0
	setb	ea
	clr	pin_dsp_busy
	mov	a,#0aah
	; Wait for mailbox to empty out.
X043c:	jb	pin_dav_pc,X043c
	; Then write 0xaa (reset successful) into the mailbox.
	movx	@r0,a

;
; Check for incoming commands. This is the start of the command monitoring
; loop, where we read commands, dispatch them, and then return back here.
;
check_cmd:
	; cmd_avail can be set in an interrupt handler in the case that we
	; receive a command while playback or recording is going on.
	jb	cmd_avail,X0446
	; Wait for the host PC to write a command to the mailbox.
wait_for_cmd:
	jnb	pin_dav_dsp,wait_for_cmd
X0446:	
	; Shut off the timer to pause any current playback/recording operation.
	clr	tr0
	; We are busy running a command, so update the status bit.
	setb	pin_dsp_busy
	clr	cmd_avail
	; Get the command from the mailbox.
	movx	a,@r0
	mov	command_byte,a
	; Fetch the most significant 4 bits, which represent the command group.
	swap	a
	anl	a,#0fh
	; Dispatch ordinary commands
	cjne	a,#0dh,dispatch_cmd
	; The command MSB was 0dh, so dispatch the miscellaneous command group.
	lcall	cmdg_misc
	jnb	et0,wait_for_cmd
	setb	tr0
	sjmp	wait_for_cmd
;
; Dispatches a command.
;
dispatch_cmd:
	; Look up the command group (4 MSBs of command byte) in the table of
	; major commands.
	mov	dptr,#table_major_cmds
	; Read the 8-bit offset for the current major command group.
	movc	a,@a+dptr
	; Jump to the table's address plus the offset we looked up.
	jmp	@a+dptr

table_major_cmds:
	.db	5ah,11h,1ah,1fh,35h,3dh,55h,14h
	.db	45h,17h,55h,55h,55h,55h,4dh,2dh
	.db	55h
; 11h: command group 1
cmdg_dac:
	ljmp	cmdg_dac_e
; 14h: command group 7
cmdg_dac2:
	ljmp	cmdg_dac2_e
; 17h: command group 9
cmdg_hs:
	ljmp	cmdg_hs_e
; 1ah: command group 2
cmdg_adc:
	clr	pin_dsp_busy
	ljmp	cmdg_adc_e
; 1fh: command group 3
cmdg_midi:
	clr	pin_dsp_busy
	jnb	pin_dma_enablel,X048b
	ljmp	do_midi_cmd
X048b:	
	; During an existing DMA operation (playback/recording) we can only
	; run the MIDI write/poll command and no others.
	jnb	command_byte_3,continue_dma_op
	ljmp	do_midi_cmd
; 2dh: command group F
cmdg_aux:
	clr	pin_dsp_busy
	jnb	pin_dma_enablel,continue_dma_op
	ljmp	cmdg_aux_e
; 35h: command group 4
cmdg_setup:
	clr	pin_dsp_busy
	jnb	pin_dma_enablel,continue_dma_op
	ljmp	cmdg_setup_e
; 3dh: command group 5
cmdg_ram_playback:
	clr	pin_dsp_busy
	jnb	pin_dma_enablel,continue_dma_op
	ljmp	cmdg_ram_playback_e
; 45h: command group 8
cmdg_silence:
	clr	pin_dsp_busy
	jnb	pin_dma_enablel,continue_dma_op
	ljmp	cmdg_silence_e
; 4dh: command group E
cmdg_ident:
	clr	pin_dsp_busy
	jnb	pin_dma_enablel,continue_dma_op
	ljmp	cmd_ident_e
; 55h: command groups 6, A, B, C, and D are unimplemented.
cmdg_invalid:
	clr	pin_dsp_busy
	jb	pin_dma_enablel,check_cmd
; 5ah: command group 0

;
; Command group 0: status
;
cmdg_status:
	clr	pin_dsp_busy
cmd_halt:
	; Command 08 is halt (infinite loop).
	jb	command_byte_3,cmd_halt
	; Command 04 is the status command
	jnb	command_byte_2,cmd_not_04
	; Grab the internal status register
	mov	a,status_register
	; Wait for the mailbox to become empty
X04c8:	jb	pin_dav_pc,X04c8
	; Output the status register.
	movx	@r0,a
	; Is DMA recording/playback going on?
	jnb	pin_dma_enablel,continue_dma_op
cmd_not_04:
	ljmp	wait_for_cmd
continue_dma_op:	
	; Reenable timer if we are processing commands during a DMA operation.
	setb	tr0
	ljmp	check_cmd
;
; Command group 4: Setup
;
cmdg_setup_e:
	; Command 48 sets the DMA block transfer size
	jb	command_byte_3,cmd_set_dma_block_size
	; Command 40 sets the time constant
	; Wait for data to become available in the mailbox.
X04da:	jnb	pin_dav_dsp,X04da
	; Get the byte
	movx	a,@r0
	; Set up the timer
	mov	th0,a
	mov	tl0,a
	; Store the time constant value
	mov	time_constant,a
	ljmp	wait_for_cmd
;
cmd_set_dma_block_size:
	; Wait for data byte to become available
	jnb	pin_dav_dsp,cmd_set_dma_block_size
	movx	a,@r0
	; This is the low byte of the DMA block length
	mov	dma_blk_len_lo,a
	; Get the next byte
X04ed:	jnb	pin_dav_dsp,X04ed
	movx	a,@r0
	; This is the high byte of the DMA block length
	mov	dma_blk_len_hi,a
	ljmp	wait_for_cmd

;
; Command group F: Auxiliary commands
;
cmdg_aux_e:
	jb	command_byte_3,cmd_sram_test
	jb	command_byte_2,cmd_checksum
	jb	command_byte_1,cmd_force_interrupt
	jb	command_byte_0,cmd_aux_status
	ljmp	cmd_sine_gen
; Command F2: Forced host PC interrupt
cmd_force_interrupt:
	clr	pin_irequest
	setb	pin_irequest
	ljmp	check_cmd
; Command F8: Test the internal SRAM from 7Fh to 00h.
cmd_sram_test:
	mov	r0,#7fh
	mov	a,#0aah
	; Write and verify AAh to SRAM
sram_test_loop1:
	mov	@r0,a
	cjne	@r0,#0aah,sram_test_end
	djnz	r0,sram_test_loop1
	mov	r0,#7fh
	mov	a,#55h
	; Write and verify 55h to SRAM
sram_test_loop2:
	mov	@r0,a
	cjne	@r0,#55h,sram_test_end
	djnz	r0,sram_test_loop2
sram_test_end:
	; Output indicates which byte failed the test or 0 for success.
	mov	a,r0
	movx	@r0,a
	ljmp	check_cmd
; Command F4: Perform ROM checksum
; r0 and r1 contain 16-bit checksum

; BUG: This routine includes the value of location 1000h in the calculation.
; Some 8051s wrap this back to address 0000h and others use the value at 1000h.
; Since the value at 0000h is 02h, the return value will be the real checksum
; + 02h. The value at 1000h is often FFh (unimplemented or unprogrammed) which
; means the return value will be the real checksum + ffh.
cmd_checksum:
	mov	r0,#0
	mov	r1,#0
	; Start at the end
	mov	dptr,#RESET
csum_loop:
	clr	a
	movc	a,@a+dptr
	; Add current ROM byte to r0
	add	a,r0
	mov	r0,a
	jnc	X0533
	; r0 add overflowed, so add it to r1
	inc	r1
X0533:	
	; Check high byte of data pointer.
	mov	a,dph
	; If it equals 10h, then we are done! Since this check happens at the
	; end, the value at 1000h gets counted in the total (see comment above)
	cjne	a,#10h,csum_not_done
	ljmp	csum_done
csum_not_done:
	; Move to the next ROM byte and continue
	inc	dptr
	sjmp	csum_loop
csum_done:
	; Send MSB of ROM checksum
	mov	a,r1
	movx	@r0,a
	mov	a,r0
	; Wait for the host to empty mailbox
X0541:	jb	pin_dav_pc,X0541
	; Send LSB of ROM checksum
	movx	@r0,a
	ljmp	check_cmd
; Command F1: Get auxiliary DSP status
cmd_aux_status:
	; Outputs port 2 contents to the host PC.
	; On the SB 2.0, the bits are assigned as follows:
	; P2.0: Mute enable (0=speaker enabled)
	; P2.1 to P2.4: Reserved
	; P2.5: Analog comparator bit for recording modes
	; P2.6: Mailbox data available for the PC to read
	; P2.7: Mailbox data available for the 8051 to read
	mov	a,p2
X054a:	jb	pin_dav_pc,X054a
	movx	@r0,a
	ljmp	check_cmd
; Command F0: Generates sine wave
cmd_sine_gen:
	; Set up pointer to sine table
	mov	r0,#40h
	mov	dptr,#sine_table
	; Copy the sine table to SRAM
gen_sine_loop:
	clr	a
	movc	a,@a+dptr
	mov	@r0,a
	inc	dptr
	inc	r0
	cjne	r0,#80h,gen_sine_loop
	; Playback pointer start point of 40h
	mov	r1,#40h
	; This is not used by the sine playback routine
	mov	ram_loops,#0ffh
	; DDS increment of 8
	mov	r7,#8
	; Set up the timer period
	mov	th0,#0c2h
	; Set the timer interrupt vector to the sine handler
	mov	dptr,#vector_sine
	mov	vector_low,dpl
	mov	vector_high,dph
	; Turn on the speaker
	clr	pin_mute_en
	; Enable the timer, and off we go!
	setb	et0
	setb	tr0
	ljmp	check_cmd

;
; Sine wave table to be used for sine wave playback.
; The table contains 64 values of a full sine wave. the midpoint is 7f, the
; maximum is ff, and the minimum is 1.
; Start of the table is at 057Ah, and the last value is at 05B9h
sine_table:
	.db	7fh,73h,67h,5bh,4fh,44h,39h,2fh
	.db	26h,1dh,16h,0fh,0ah,6,3,1
	.db	1,1,3,6,0ah,0fh,16h,1dh
	.db	26h,2fh,39h,44h,4fh,5bh,67h,73h
	.db	80h,8ch,98h,0a4h,0b0h,0bbh,0c6h,0d0h
	.db	0d9h,0e2h,0e9h,0f0h,0f5h,0f9h,0fch,0feh
	.db	0ffh,0feh,0fch,0f9h,0f5h,0f0h,0e9h,0e2h
	.db	0d9h,0d0h,0c6h,0bbh,0b0h,0a4h,98h,8ch

;
; Command group 3: MIDI commands
;
do_midi_cmd:
	jb	command_byte_3,cmd_midi_write_poll
	jnb	command_byte_2,cmd_midi_read_write_poll
	; Command 30: MIDI read poll.
	; Set up warm boot magic number so we can continue where we left off
	; after receiving a DSP reset command.
	mov	warmboot_magic1,#34h
	mov	warmboot_magic2,#12h
	ljmp	cmd_midi_read_write_poll
; Command 38: MIDI write poll.
cmd_midi_write_poll:
	; Wait for last serial transfer to complete
	jnb	ti,cmd_midi_write_poll
	; Clear transfer interrupt bit
	clr	ti
	; Wait for host PC to send us data
X05ce:	jnb	pin_dav_dsp,X05ce
	movx	a,@r0
	; Data arrived, so start serial transfer
	setb	tr0
	mov	sbuf,a
	ljmp	check_cmd
; Commands 34 to 37: MIDI read/write poll with optional time stamp and
; interrupt.
; Registers:
; r0: Write pointer to SRAM buffer
; r1: Read pointer to SRAM buffer
; r4: Bytes remaining in SRAM buffer
; r5, r6, r7: MIDI time stamp value
cmd_midi_read_write_poll:
	jnb	command_byte_1,skip_midi_timestamp_setup
	; Set up timer for MIDI time stamping
	mov	tmod,#21h
	setb	midi_timestamp
	mov	tl0,#17h
	mov	th0,#0fch
	mov	r5,#0
	mov	r6,#0
	mov	r7,#0
	setb	et0
	setb	tr0
skip_midi_timestamp_setup:
	; Clear the receive data buffer
	mov	a,sbuf
	clr	ri
	; Initialize write pointer
	mov	r0,#40h
	; Initialize read pointer
	mov	r1,#40h
	; Initialize bytes remaining counter
	mov	r4,#40h
	ljmp	midi_check_for_input_data
midi_main_loop:
	jnb	ti,midi_check_for_input_data
	jnb	pin_dav_dsp,midi_check_for_input_data
	movx	a,@r0
	jb	command_byte_2,midi_write_poll
	mov	r0,#40h
	mov	r1,#40h
	mov	r4,#40h
	clr	et0
	clr	tr0
	mov	warmboot_magic1,#0
	mov	warmboot_magic2,#0
	clr	midi_timestamp
	mov	tmod,#22h
	ljmp	check_cmd
;
midi_write_poll:
	clr	ti
	mov	sbuf,a
midi_check_for_input_data:
	; Check to see if there is data in the serial input buffer
	jb	ri,midi_has_input_data
	cjne	r4,#40h,X062c
	sjmp	midi_main_loop
;
X062c:	jnb	pin_dav_pc,midi_flush_buffer_to_host
	sjmp	midi_main_loop
midi_has_input_data:
	; There is data in the serial data buffer.
	; Check to see if we need to add a time stamp.
	jnb	command_byte_1,midi_read_no_timestamp
	; Stop the timer
	clr	tr0
	mov	a,r5
	cjne	r4,#0,midi_write_r5
	sjmp	midi_nowrap_writebuffer
;
midi_write_r5:
	; Append LSB of MIDI time stamp to data buffer.
	mov	@r0,a
	inc	r0
	dec	r4
	cjne	r0,#80h,midi_nowrap_writebuffer
	; Wrap to the start of the buffer at 40h
	mov	r0,#40h
midi_nowrap_writebuffer:
	mov	a,r6
	cjne	r4,#0,midi_write_r6
	sjmp	X0652
midi_write_r6:
	; Append middle byte of MIDI time stamp to data buffer.
	mov	@r0,a
	inc	r0
	dec	r4
	; Wrap if needed
	cjne	r0,#80h,X0652
	mov	r0,#40h
X0652:	mov	a,r7
	cjne	r4,#0,midi_write_r7
	sjmp	X0660
midi_write_r7:
	; Append LSB of MIDI time stamp to data buffer
	mov	@r0,a
	inc	r0
	dec	r4
	cjne	r0,#80h,X0660
	mov	r0,#40h
	; Turn the timer on again
X0660:	setb	tr0
midi_read_no_timestamp:
	; Copy a data byte out of the serial data register
	mov	a,sbuf
	; Store it to the serial buffer if there is space remaining.
	cjne	r4,#0,midi_store_read_data_to_buffer
	; Ran out of space, so just drop the byte.
	sjmp	midi_ready_to_receive_more
midi_store_read_data_to_buffer:
	; Store the received data to the SRAM.
	mov	@r0,a
	inc	r0
	dec	r4
	cjne	r0,#80h,midi_ready_to_receive_more
	mov	r0,#40h
midi_ready_to_receive_more:
	; Clear receive flag, we are ready to receive more data.
	clr	ri
	sjmp	midi_main_loop
; BUG: There's no way to get to this code.
	cjne	r4,#40h,midi_space_in_buffer
	ljmp	midi_nowrap_readbuffer
midi_space_in_buffer:
	mov	@r0,a
	inc	r0
	dec	r4
	cjne	r0,#80h,midi_flush_buffer_to_host
	mov	r0,#40h
; END BUG.
midi_flush_buffer_to_host:
	; Send contents of entire SRAM buffer to host PC.
	mov	a,@r1
	inc	r1
	; More space is available now
	inc	r4
	; When we hit the end, wrap back to the beginning
	cjne	r1,#80h,midi_nowrap_readbuffer
	mov	r1,#40h
midi_nowrap_readbuffer:
	; Place MIDI data byte in mailbox
	movx	@r0,a
	; Optionally, send an interrupt to the host PC.
	jnb	command_byte_0,midi_skip_interrupt
	clr	pin_irequest
	setb	pin_irequest
midi_skip_interrupt:
	; Done, go back to the main loop
	ljmp	midi_main_loop
; Command group 2: Recording commands
cmdg_adc_e:
	jb	command_byte_3,cmd_adc_autoinit_direct
	jb	command_byte_2,cmd_adc_dma
	ljmp	cmd_adc_direct
; Command 28h: Auto-init direct ADC
cmd_adc_autoinit_direct:
	setb	dma_autoinit_on
	mov	len_left_lo,dma_blk_len_lo
	mov	len_left_hi,dma_blk_len_hi
	ljmp	X06ca
; Command 24h: DMA ADC
cmd_adc_dma:
	jb	pin_dma_enablel,X06be
	; DMA operation is already going on
	; Wait for additional byte from host PC.
X06ad:	jnb	pin_dav_dsp,X06ad
	; Read length_low argument
	movx	a,@r0
	mov	length_low,a
	; Read length_high argument
X06b3:	jnb	pin_dav_dsp,X06b3
	movx	a,@r0
	mov	length_high,a
	setb	dma_mode_on
	ljmp	X06ca
	; Set up DMA from scratch
	; Read argument byte: length low
X06be:	jnb	pin_dav_dsp,X06be
	movx	a,@r0
	mov	len_left_lo,a
	; Read argument byte: length high
X06c4:	jnb	pin_dav_dsp,X06c4
	movx	a,@r0
	mov	len_left_hi,a
X06ca:
	; Turn on DMA enable line
	clr	pin_dma_enablel
	; Set up interrupt vector
	mov	dptr,#vector_dma_adc
	mov	vector_low,dpl
	mov	vector_high,dph
	setb	mode_dma_adc
	; Start the sample timer!
	setb	tr0
	setb	et0
	ljmp	check_cmd
; Command 20: Direct ADC. This immediately takes one sample and returns it.
cmd_adc_direct:
	; Start DAC at half code, then run SAR algorithm.
	mov	port_dac_out,#80h
	nop	
	nop	
	jnb	pin_adc_comp,X06e8
	clr	pin_dac_7
X06e8:	setb	pin_dac_6
	nop	
	nop	
	jnb	pin_adc_comp,X06f1
	clr	pin_dac_6
X06f1:	setb	pin_dac_5
	nop	
	jnb	pin_adc_comp,X06f9
	clr	pin_dac_5
X06f9:	setb	pin_dac_4
	nop	
	jnb	pin_adc_comp,X0701
	clr	pin_dac_4
X0701:	setb	pin_dac_3
	nop	
	jnb	pin_adc_comp,X0709
	clr	pin_dac_3
X0709:	setb	pin_dac_2
	nop	
	jnb	pin_adc_comp,X0711
	clr	pin_dac_2
X0711:	setb	pin_dac_1
	nop	
	jnb	pin_adc_comp,X0719
	clr	pin_dac_1
X0719:	setb	pin_dac_0
	nop	
	jnb	pin_adc_comp,X0721
	clr	pin_dac_0
	; DAC port now contains acquired sample
X0721:	mov	a,port_dac_out
	; Make sure mailbox is empty
X0723:	jb	pin_dav_pc,X0723
	; Output sample to the host PC.
	movx	@r0,a
	clr	pin_dsp_busy
	ljmp	check_cmd
; Command group 9: High speed record and playback
cmdg_hs_e:
	setb	high_speed
	jnb	command_byte_3,X0736
; Command 98h: High speed record
	setb	record_mode
	ljmp	X0738
; Command 90h: High speed playback
X0736:	 
	clr	record_mode
X0738:
	; Store DMA block length in temporary registers
	mov	r6,dma_blk_len_lo
	mov	r7,dma_blk_len_hi
	; Set up warm boot magic numbers
	mov	warmboot_magic1,#34h
	mov	warmboot_magic2,#12h
	; Turn on DMA
	clr	pin_dma_enablel
	; Turn on sample timer
	setb	et0
	setb	tr0
	; Wait for timer interrupt to fire
X0748:	jb	et0,X0748
	clr	pin_dsp_busy
	ljmp	check_cmd
; Command group 1: Audio playback
cmdg_dac_e:
	jb	command_byte_3,cmd_dac_autoinit
	jb	command_byte_2,cmd_dac_dma
	ljmp	cmd_dac_direct
; Command 18h: DMA playback with auto init DMA.
cmd_dac_autoinit:
	setb	dma_autoinit_on
	mov	len_left_lo,dma_blk_len_lo
	mov	len_left_hi,dma_blk_len_hi
	ljmp	X078e
; Command 14h: DMA playback
cmd_dac_dma:
	jb	pin_dma_enablel,X077e
	; If DMA is already running, then get updated length value from PC.
	clr	pin_dsp_busy
	; Get length low value from host PC
X0769:	jnb	pin_dav_dsp,X0769
	movx	a,@r0
	mov	length_low,a
	; Get length high value from host PC
X076f:	jnb	pin_dav_dsp,X076f
	movx	a,@r0
	mov	length_high,a
	setb	dma_mode_on
	setb	et0
	setb	tr0
	ljmp	check_cmd
	; DMA is not already running, so set it up from scratch
X077e:	clr	pin_dsp_busy
	; Get length low value
X0780:	jnb	pin_dav_dsp,X0780
	movx	a,@r0
	mov	len_left_lo,a
	; Get length high value
X0786:	jnb	pin_dav_dsp,X0786
	movx	a,@r0
	mov	len_left_hi,a
	setb	pin_dsp_busy
	; Enable DMA
X078e:	clr	pin_dma_enablel
	jb	command_byte_1,cmd_dac_dma_use_adpcm_2
	; Set up standard 8-bit DMA DAC vector
	mov	dptr,#vector_dma_dac_8
	setb	mode_dma_dac
	ljmp	X07c0

; Command 16h: DMA DAC with 2-bit ADPCM.
; Registers:
; r2 = ADPCM output sample
; r3 = Number of packed samples remaining in the current data byte
; r5 = ADPCM accumulator
; r6 = Current data byte being decoded
cmd_dac_dma_use_adpcm_2:
	; Set up 2-bit ADPCM vector
	mov	dptr,#vector_dma_dac_adpcm2
	setb	mode_adpcm2
	; If command 17h, fetch first byte as reference value
	jb	command_byte_0,cmd_dac_dma_use_reference
	; Trigger host PC DMA
	setb	pin_drequest
	clr	pin_drequest
	; Wait for value to arrive
X07a7:	jnb	pin_dav_dsp,X07a7
	; Store it
	movx	a,@r0
	mov	r6,a
	mov	r3,#4
	ljmp	X07c0
cmd_dac_dma_use_reference:
	; Trigger DMA to fetch value from host PC
	setb	pin_drequest
	clr	pin_drequest
	; Wait for it to arrive
X07b5:	jnb	pin_dav_dsp,X07b5
	; Write out value to DAC and store it as reference
	movx	a,@r0
	mov	r2,a
	mov	port_dac_out,a
	; Set ADPCM accumulator to 1
	mov	r5,#1
	; Set remaining sample count to 1
	mov	r3,#1
X07c0:
	; Set up interrupt vector and start the timer
	mov	vector_low,dpl
	mov	vector_high,dph
	clr	pin_dsp_busy
	setb	et0
	setb	tr0
	ljmp	check_cmd
; Command 10h: Direct DAC.
cmd_dac_direct:
	clr	pin_dsp_busy
	; Wait for byte from host PC
X07d1:	jnb	pin_dav_dsp,X07d1
	; Write the byte out to the DAC, and we are done. Simple.
	movx	a,@r0
	mov	port_dac_out,a
	ljmp	check_cmd
; Command group 7. ADPCM DAC output commands.
cmdg_dac2_e:
	jb	command_byte_3,cmd_dac_autoinit_adpcm
	jb	command_byte_2,cmd_dac_adpcm
; Command 78: Auto-init DMA ADPCM
cmd_dac_autoinit_adpcm:
	setb	dma_autoinit_on
	mov	len_left_lo,dma_blk_len_lo
	mov	len_left_hi,dma_blk_len_hi
	ljmp	X0815
; Command 74: Standard DMA ADPCM
cmd_dac_adpcm:
	jb	pin_dma_enablel,X0805
	; DMA is already running, so update block length
	clr	pin_dsp_busy
	; Get low byte from host PC
X07f0:	jnb	pin_dav_dsp,X07f0
	movx	a,@r0
	mov	length_low,a
	; Get high byte
X07f6:	jnb	pin_dav_dsp,X07f6
	movx	a,@r0
	mov	length_high,a
	setb	dma_mode_on
	setb	et0
	setb	tr0
	ljmp	check_cmd
X0805:	
	; DMA is not running, so set up the transfer from scratch
	clr	pin_dsp_busy
	; Get length low byte
X0807:	jnb	pin_dav_dsp,X0807
	movx	a,@r0
	mov	len_left_lo,a
	; Get length high byte
X080d:	jnb	pin_dav_dsp,X080d
	movx	a,@r0
	mov	len_left_hi,a
	setb	pin_dsp_busy
	; Turn on DMA
X0815:	clr	pin_dma_enablel
	; Commands 74, 78: 4-bit ADPCM
	jnb	command_byte_1,cmd_dac_adpcm_use_4bit
	; Commands 76, 7A: 2.6-bit ADPCM
	mov	dptr,#vector_dma_dac_adpcm2_6
	setb	mode_adpcm26
	ljmp	X0827
cmd_dac_adpcm_use_4bit:
	; Set up timer interrupt vector appropriately
	mov	dptr,#vector_dma_dac_adpcm4
	setb	mode_adpcm4
X0827:	
	; Least significant bit of command byte indicates reference mode
	jnb	command_byte_0,dac_no_reference
	; Trigger early DMA request to get reference byte
	setb	pin_drequest
	clr	pin_drequest
	; Wait for it to come in
X082e:	jnb	pin_dav_dsp,X082e
	; Store it to the DAC and to r2
	movx	a,@r0
	mov	r2,a
	mov	port_dac_out,a
	; Initialize ADPCM accumulator to 1.
	mov	r5,#1
	; Set up remaining samples per byte to 1.
	mov	r3,#1
	ljmp	X084f
dac_no_reference:
	; No reference value, so we trigger DMA to get first
	; actual sample data byte
	setb	pin_drequest
	clr	pin_drequest
	; Wait for it to come in
X0840:	jnb	pin_dav_dsp,X0840
	; Store it in current sample byte (r6)
	movx	a,@r0
	mov	r6,a
	; Depending on 2.6-bit or 4-bit mode, we have a differing number of
	; samples to process in the incoming data byte.
	jnb	command_byte_1,dac_no_ref_adpcm4
	mov	r3,#3
	ljmp	X084f
;
dac_no_ref_adpcm4:
	mov	r3,#4
	; Set up interrupt vector, start timer.
X084f:	mov	vector_low,dpl
	mov	vector_high,dph
	clr	pin_dsp_busy
	setb	et0
	setb	tr0
	ljmp	check_cmd

; Command 80: Generate silence
cmdg_silence_e:
	; Turn on DMA enable (probably unnecessary)
	clr	pin_dma_enablel
	; Get length low from host PC
X0860:	jnb	pin_dav_dsp,X0860
	movx	a,@r0
	mov	len_left_lo,a
	; Get length high from host PC
X0866:	jnb	pin_dav_dsp,X0866
	movx	a,@r0
	mov	len_left_hi,a
	; Set up interrupt vector, start timer
	mov	dptr,#vector_dac_silence
	mov	vector_low,dpl
	mov	vector_high,dph
	setb	mode_silence
	setb	et0
	setb	tr0
	ljmp	check_cmd
; Command group 5: SRAM playback
cmdg_ram_playback_e:
	jb	command_byte_3,cmd_ram_load
	jb	command_byte_0,cmd_ram_playback
	ljmp	cmd_stop_ram_playback
; Command 58: Load data into SRAM
cmd_ram_load:
	; Wait for data to come in
	jnb	pin_dav_dsp,cmd_ram_load
	; Fetch the argument: the number of RAM samples times two
	movx	a,@r0
	mov	ram_samples_x2,a
	; Fetch the argument: playback loop count
X088d:	jnb	pin_dav_dsp,X088d
	movx	a,@r0
	mov	ram_pb_count,a
	; Fetch the argument: playback loop count (2)
X0893:	jnb	pin_dav_dsp,X0893
	movx	a,@r0
	mov	ram_pb_count2,a
	; Fetch an unused argument.
X0899:	jnb	pin_dav_dsp,X0899
	movx	a,@r0
	mov	ram_pb_unused,a
	mov	r0,#40h
	; Fetch the samples
X08a1:	jnb	pin_dav_dsp,X08a1
	movx	a,@r0
	mov	@r0,a
	inc	r0
	dec	ram_samples_x2
	djnz	ram_samples_x2,X08a1
	; Done fetching all the samples, so reset the data pointer
	mov	r1,#40h
	; Command 59 fetches the samples and then immediately plays them back.
	jb	command_byte_0,cmd_ram_playback
	ljmp	check_cmd
; Command 51: Plays back samples stored in SRAM.
cmd_ram_playback:
	setb	pin_dsp_busy
	; Makes a copy of the arguments
	mov	ram_loops2,ram_pb_count
	; BUG: This probably should copy ram_loops to another variable.
	mov	ram_loops,ram_loops
	mov	ram_pb_unused2,ram_pb_unused
	mov	ram_smps_left,ram_samples_x2
	; Set up SRAM playback timer interrupt vector, then enable the timer.
	mov	dptr,#vector_cmd_ram_playback
	mov	vector_low,dpl
	mov	vector_high,dph
	setb	et0
	setb	tr0
	ljmp	check_cmd
; Command 50: Stops playback of SRAM samples
cmd_stop_ram_playback:
	; Shut off the timer
	clr	et0
	clr	tr0
	ljmp	check_cmd

; Command group D: Miscellaneous commands
cmdg_misc:
	jb	command_byte_2,cmd_dma_continue
	jb	command_byte_3,cmd_spk_stat
	jb	command_byte_0,cmd_speaker_en_dis
	clr	et0
	clr	tr0
	setb	pin_dma_enablel
	ljmp	cmdg_misc_exit
; Command D4: Continue DMA operation
cmd_dma_continue:
	; Enable DMA
	clr	pin_dma_enablel
	; Turn on timer
	setb	et0
	setb	tr0
	ljmp	cmdg_misc_exit
; Command D8: Speaker status
cmd_spk_stat:
	jb	command_byte_1,cmd_exit_autoinit
	jnb	pin_mute_en,X08fe
	clr	a
	ljmp	X0900
;
X08fe:	mov	a,#0ffh
	; Wait for mailbox to empty out
X0900:	jb	pin_dav_pc,X0900
	; Send speaker status. FFh=enabled, 00h=disabled
	movx	@r0,a
	ljmp	cmdg_misc_exit
; Command DA: Exit auto-init DMA operation
cmd_exit_autoinit:
	; Set the flag for the interrupt handler to check
	setb	autoinit_exit
	ljmp	cmdg_misc_exit
; Command D1: Enable speaker
cmd_speaker_en_dis:
	jb	command_byte_1,X0915
	lcall	cmd_speaker_on
	ljmp	cmdg_misc_exit
; Command D3: Disable speaker
X0915:	lcall	cmd_speaker_off
cmdg_misc_exit:
	clr	pin_dsp_busy
	ret	
;
cmd_speaker_on:
	push	acc
	clr	pin_dsp_busy
	; Set DAC to 0.
	mov	port_dac_out,#0
	; Turn on speaker
	clr	pin_mute_en
	; From 0, ramp up the DAC code to 81h
	mov	a,#0
X0926:	mov	port_dac_out,a
	inc	a
	; Delay loop
	mov	r3,#30h
X092b:	djnz	r3,X092b

	cjne	a,#81h,X0926
	; Speaker is now on
	setb	spkr_on
	pop	acc
	ret	
;
cmd_speaker_off:
	push	acc
	clr	pin_dsp_busy
	; Get current DAC value so we can ramp it down slowly.
	mov	a,port_dac_out
	jz	X0947
X093d:	mov	port_dac_out,a
	dec	a
	; Delay
	mov	r3,#30h
X0942:	djnz	r3,X0942
	; Decrement until we hit zero
	cjne	a,#0,X093d
	; Mute the speaker
X0947:	setb	pin_mute_en
	; Speaker is now muted.
	clr	spkr_on
	pop	acc
	ret	

; Command group E: DSP identification
cmd_ident_e:
	jb	command_byte_3,cmd_read_test_reg
	jb	command_byte_2,cmd_write_test_reg
	jb	command_byte_1,cmd_dsp_dma_id
	jb	command_byte_0,cmd_dsp_version
X095a:	jnb	pin_dav_dsp,X095a
	movx	a,@r0
	cpl	a
X095f:	jb	pin_dav_pc,X095f
	movx	@r0,a
	ljmp	check_cmd

; Command E8: Read test register
cmd_read_test_reg:
	; Read test register (2Ah)
	mov	a,2ah
	; Send to host PC
X0968:	jb	pin_dav_pc,X0968
	movx	@r0,a
	ljmp	check_cmd

; Command E4: Write test register
cmd_write_test_reg:
	; Wait for data to arrive
	jnb	pin_dav_dsp,cmd_write_test_reg
	; Write it out to test register (2Ah)
	movx	a,@r0
	mov	2ah,a
	ljmp	check_cmd

; Command E2: Firmware validation check. Uses challenge/response algorithm.
cmd_dsp_dma_id:
	; Wait for challenge byte to come in
	jnb	pin_dav_dsp,cmd_dsp_dma_id
	movx	a,@r0
	setb	pin_dsp_busy
	; Turn on DMA
	clr	pin_dma_enablel
	; Perform magical incantation on challenge byte using two values that
	; are initialized as follows:
	; dsp_dma_id0 = AAh
	; dsp_dma_id1 = 96h

	; dsp_dma_id0 += dsp_dma_id1 XOR challenge_byte
	xrl	a,dsp_dma_id1
	add	a,dsp_dma_id0
	mov	dsp_dma_id0,a
	; dsp_dma_id1 = dsp_dma_id1 >> 2 (actually a rotate)
	mov	a,dsp_dma_id1
	rr	a
	rr	a
	mov	dsp_dma_id1,a
	; Get current value of dsp_dma_id0 and send it to host PC (response)
	mov	a,dsp_dma_id0
X098e:	jb	pin_dav_pc,X098e
	movx	@r0,a
	; Trigger DMA
	setb	pin_drequest
	clr	pin_drequest
	; Wait for it to be sent
X0996:	jb	pin_dav_pc,X0996
	nop	
	; Disable DMA
	setb	pin_dma_enablel
	clr	pin_dsp_busy
	ljmp	check_cmd
; Command E1: Get DSP version
cmd_dsp_version:
	; Locate dsp version number
	mov	dptr,#dsp_version
	clr	a
	movc	a,@a+dptr
	; Transmit major version number
X09a6:	jb	pin_dav_pc,X09a6
	movx	@r0,a
	mov	a,#1
	movc	a,@a+dptr
	; Transmit minor version number
X09ad:	jb	pin_dav_pc,X09ad
	movx	@r0,a
	ljmp	check_cmd

; **************
; ADPCM routines
; **************

; Register uses:
; r2 = ADPCM output sample
; r3 = Number of packed samples remaining in current data byte
; r5 = ADPCM accumulator
; r6 = Current data byte being decoded

;
; ADPCM 2-bit decode routine
;
adpcm_2_decode:
	; Take current data byte, examine
	; the two MSBs.
	mov	a,r6
	rlc	a
	jc	adpcm_2_decode_negative
; Sign bit is positive, so continue
	rlc	a
	; Store it back to the data byte since we know the two MSBs now.
	mov	r6,a
	mov	a,r5
	jc	X09cc
	; So far the value is 00.
	; delta = r5 / 2
	rrc	a
	mov	r5,a
	jnz	X09c4
	; If r5 = 0, then set it to 1.
	inc	r5
	sjmp	adpcm_2_output
X09c4:	
	; r5 != 0 case
	; Add delta to output sample, then store in output sample.
	add	a,r2
	jnc	X09c9
	; If there is a carry out, then saturate it to FF.
	; BUG: this should be #0ffh.
	mov	a,0ffh
X09c9:	mov	r2,a
	sjmp	adpcm_2_output
X09cc:
	; The value is 01.
	clr	c
	; delta = (r5 / 2) + r5
	rrc	a
	add	a,r5
	; Add delta to output sample
	add	a,r2
	jnc	X09d4
	; If there is a carry out, saturate it to ffh.
	mov	a,#0ffh
X09d4:	
	; Store the result back to the output sample
	mov	r2,a
	cjne	r5,#20h,X09da
	sjmp	adpcm_2_output
X09da:	
	; If the ADPCM accumulator != 20h, then multiply it by two.
	mov	a,r5
	add	a,r5
	mov	r5,a
	sjmp	adpcm_2_output
; Incoming bits are either 10 or 11. (sign bit is negative)
adpcm_2_decode_negative:
	; Get the next bit
	rlc	a
	; Save r6 shifted left by two, lining up the next 2 bits for us.
	mov	r6,a
	; Get the ADPCM accumulator value
	mov	a,r5
	jc	X09f4
; Incoming bits are 10.
	rrc	a
	; delta = r5 / 2
	mov	r5,a
	jnz	X09eb
	; If ADPCM accumulator is 0, set it to 1.
	inc	r5
	sjmp	adpcm_2_output
X09eb:	
	; a = Current output sample - delta
	xch	a,r2
	clr	c
	subb	a,r2
	jnc	X09f1
	; Saturate the result at 0 if a borrow occurred.
	clr	a
X09f1:	
	; Output the resulting sample
	mov	r2,a
	sjmp	adpcm_2_output
; Incoming bits are 11.
X09f4:	clr	c
	rrc	a
	; delta = (r5 / 2) + r5
	add	a,r5
	xch	a,r2
	clr	c
	; a = Current output sample - delta
	subb	a,r2
	jnc	X09fd
	; Saturate the result at 0 if a borrow occurred.
	clr	a
X09fd:	
	; Output the result.
	mov	r2,a
	cjne	r5,#20h,X0a03
	sjmp	adpcm_2_output
;
X0a03:
	; If the ADPCM accumulator != 20h, then multiply it by two.
	mov	a,r5
	add	a,r5
	mov	r5,a
adpcm_2_output:
	mov	port_dac_out,r2
	ret

;
; ADPCM 4-bit decode routine
;
adpcm_4_decode:
	mov	a,r5
	clr	c
	rrc	a
	; 27h <- r5 / 2
	mov	27h,a
	; rb3r1 <- r6 (current data byte)
	mov	a,r6
	mov	rb3r1,a
	; Get the most significant nybble of the incoming data
	swap	a
	; Store it back (we process the other nybble later)
	mov	r6,a
	; Mask off the three least significant bits
	anl	a,#7
	; Store in 28h
	mov	28h,a
	; delta = nybble * ADPCM accumulator + (ADPCM accumulator / 2)
	mov	b,r5
	mul	ab
	add	a,27h
	; And store the result in 29h (delta)
	mov	29h,a
	; Grab original data byte again
	mov	a,rb3r1
	rlc	a
	; Check MSB (the sign bit)
	jc	X0a2d
	; MSB is zero, so value is positive. Add the delta to the
	; current sample output value.
	mov	a,29h
	add	a,r2
	jnc	X0a34
	; Saturate it to FFh if there was a carry.
	mov	a,#0ffh
	ljmp	X0a34
;
X0a2d:
	; Sign bit is negative
	mov	a,r2
	; Subtract the delta from the current sample output value.
	clr	c
	subb	a,29h
	jnc	X0a34
	; Saturate it at 00h if there was a borrow.
	clr	a
X0a34:
	; Set the new sample output value to what we calculated just now
	mov	r2,a
	; Check original 4-bit value to see if it is zero
	mov	a,28h
	jz	X0a48
	; It is not zero, so subtract five
	clr	c
	subb	a,#5
	jc	adpcm_4_output
	; Take ADPCM accumulator, multiply by two.
	mov	a,r5
	rl	a
	cjne	a,#10h,X0a4e
	; If it is 10h, make it 8.
	mov	a,#8
	ljmp	X0a4e
;
X0a48:
	; Value coming in is zero
	; Get old r5/2 value
	mov	a,27h
	; Store it to r5 unless it's zero; in that case, set r5=1.
	jnz	X0a4e
	mov	a,#1
X0a4e:
	; Store ADPCM accumulator
	mov	r5,a
adpcm_4_output:
	mov	port_dac_out,r2
	ret	

;
; ADPCM 2.6-bit decode routine
;
adpcm_2_6_decode:
	; 27h = r5 / 2.
	mov	a,r5
	clr	c
	rrc	a
	mov	27h,a
	; rb3r1 = r6 (Store off incoming data)
	mov	a,r6
	mov	rb3r1,a
	; Grab two bits
	rl	a
	rl	a
	cjne	r3,#1,X0a64
	; Bytes remaining = 1, this is the special case
	; Throw away everything except for the LSB.
	anl	a,#1
	ljmp	X0a65
;
X0a64:	
	; "Normal" case where bytes remaining != 1.
	; Grab the 3rd bit
	rl	a
X0a65:	
	; Store back to current data byte (so we can grab the next one next
	; time around).
	mov	r6,a
	; Mask off so we just have the three bits we want
	anl	a,#3
	; Store in 28h
	mov	28h,a
	; Fetch ADPCM accumulator, multiply it by our 3-bit value
	mov	b,r5
	mul	ab
	; Take LSB of result and add r5 / 2 to it.
	; delta = bits * ADPCM accumulator + (ADPCM accumulator / 2)
	add	a,27h
	; Store the result in 29h (delta)
	mov	29h,a
	; Get the original version of our incoming data byte back 
	mov	a,rb3r1
	rlc	a
	; Check the sign bit
	jc	X0a80
	; Positive, so get our result again and add it to the current output
	; sample
	mov	a,29h
	add	a,r2
	jnc	X0a87
	; Saturate it at FFh if there was a carry.
	mov	a,#0ffh
	ljmp	X0a87
;
X0a80:
	; Sign bit is negative so we subtract it from our current output sample
	mov	a,r2
	clr	c
	subb	a,29h
	jnc	X0a87
	; Saturate it at 00h if there was a borrow.
	clr	a
X0a87:	
	; Store it back to the current output sample
	mov	r2,a
	; Get the three bits again
	mov	a,28h
	jz	X0a9a
	cjne	a,#3,adpcm_2_6_output
	; The three bits were 011, so check our accumulator
	cjne	r5,#10h,X0a95
	; ADPCM accumulator is 10h, so just output the sample
	ljmp	adpcm_2_6_output
;
X0a95:	
	; ADPCM accumulator wasn't 10h, so multiply it by two.
	mov	a,r5
	rl	a
	ljmp	X0aa0
;
X0a9a:	
	; Original 3 bits were 000
	mov	a,27h
	; New reference value (r5) becomes r5 / 2 unless it was 0, in which
	; case it becomes 1.
	jnz	X0aa0
	mov	a,#1
X0aa0:	
	; Store ADPCM accumulator
	mov	r5,a
adpcm_2_6_output:
	mov	port_dac_out,r2
	ret	

;
; Copyright notice
;
	.db	"COPYRIGHT(C) CREATIVE TECHNOLOGY"
	.db	" PTE. LTD. (1991) "

;
; Unused data. Perhaps this was some sort of ADPCM lookup table?
;
	.db	0e9h,0e5h,0fah,0f3h,0f8h,0e3h,0edh,0e2h
	.db	0feh,82h,0e9h,83h,8ah,0e9h,0f8h,0efh
	.db	0ebh,0feh,0e3h,0fch,0efh,8ah,0feh,0efh
	.db	0e9h,0e2h,0e4h,0e5h,0e6h,0e5h,0edh,0f3h
	.db	8ah,0fah,0feh,0efh,84h,8ah,0e6h,0feh
	.db	0eeh,84h,8ah,82h,9bh,93h,92h,93h
	.db	83h

;
; Stored DSP version number
;
dsp_version:
	.db	2,2


;
; Padding
;
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh
	.db 0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh,0ffh